/*
 * Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
 *
 * This file is part of Loop Habit Tracker.
 *
 * Loop Habit Tracker is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Loop Habit Tracker is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.isoron.uhabits

import android.content.Context
import android.os.Build
import android.os.Environment
import android.view.WindowManager
import org.isoron.uhabits.inject.AppContext
import java.io.BufferedReader
import java.io.File
import java.io.FileWriter
import java.io.IOException
import java.io.InputStreamReader
import java.text.SimpleDateFormat
import java.util.Date
import java.util.LinkedList
import java.util.Locale
import javax.inject.Inject

open class AndroidBugReporter @Inject constructor(@AppContext private val context: Context) {

    /**
     * Captures and returns a bug report. The bug report contains some device
     * information and the logcat.
     *
     * @return a String containing the bug report.
     * @throws IOException when any I/O error occur.
     */
    @Throws(IOException::class)
    fun getBugReport(): String {
        var log = "---------- BUG REPORT BEGINS ----------\n"
        log += "${getLogcat()}\n"
        log += "${getDeviceInfo()}\n"
        log += "---------- BUG REPORT ENDS ------------\n"
        return log
    }

    @Throws(IOException::class)
    fun getLogcat(): String {
        val maxLineCount = 250
        val builder = StringBuilder()
        val process = Runtime.getRuntime().exec(arrayOf("logcat", "-d"))
        val inputReader = InputStreamReader(process.inputStream)
        val bufferedReader = BufferedReader(inputReader)
        val log = LinkedList<String>()
        var line: String?
        while (true) {
            line = bufferedReader.readLine()
            if (line == null) break
            log.addLast(line)
            if (log.size > maxLineCount) log.removeFirst()
        }
        for (l in log) {
            builder.appendLine(l)
        }
        return builder.toString()
    }

    /**
     * Captures a bug report and saves it to a file in the SD card.
     *
     * The contents of the file are generated by the method [ ][.getBugReport]. The file is saved
     * in the apps's external private storage.
     *
     * @return the generated file.
     * @throws IOException when I/O errors occur.
     */
    fun dumpBugReportToFile() {
        try {
            val date = SimpleDateFormat("yyyy-MM-dd HHmmss", Locale.US).format(Date())
            val dir = AndroidDirFinder(context).getFilesDir("Logs")
                ?: throw IOException("log dir should not be null")
            val logFile = File(String.format("%s/Log %s.txt", dir.path, date))
            val output = FileWriter(logFile)
            output.write(getBugReport())
            output.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    private fun getDeviceInfo(): String {
        val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        return buildString {
            appendLine("App Version Name: ${BuildConfig.VERSION_NAME}")
            appendLine("App Version Code: ${BuildConfig.VERSION_CODE}")
            appendLine("OS Version: ${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})")
            appendLine("OS API Level: ${Build.VERSION.SDK_INT}")
            appendLine("Device: ${Build.DEVICE}")
            appendLine("Model (Product): ${Build.MODEL} (${Build.PRODUCT})")
            appendLine("Manufacturer: ${Build.MANUFACTURER}")
            appendLine("Other tags: ${Build.TAGS}")
            appendLine("Screen Width: ${wm.defaultDisplay.width}")
            appendLine("Screen Height: ${wm.defaultDisplay.height}")
            appendLine("External storage state: ${Environment.getExternalStorageState()}")
            appendLine()
        }
    }
}
